
using System;
using System.Collections.Generic;
using System.Drawing;
using System.IO;
using System.Runtime.Serialization;
using System.Runtime.Serialization.Formatters.Binary;
using OhioState.AssetManaged;

namespace OhioState.Graphics.Proxy
{
    /// <summary>
    /// handler if bitmap changes
    /// </summary>
    /// <param name="obj">bitmap</param>
    public delegate void BitmapHandler(Bitmap obj);
    /// <summary>
    /// Layer Class that holds the result of any rendering
    /// </summary>
    [Serializable()]
    public class LayerProxy : SharedAsset, ILayer, IDrawable, System.ICloneable
    {
        private IFrameBufferObject frameBuffer;
        //public LayerProxy(String name)
        //    : this(name, new FrameBufferObjectProxy(name, )
        //{
        //}
        public LayerProxy(String name, IFrameBufferObject frameBuffer) : base(name)
        {
            _camera = new Camera();
            _active = true;
            _image = new Bitmap(_width, _height);
            _drawableProxies = new List<IDrawable>();
            this.frameBuffer = frameBuffer;
            _material = MaterialProxy.White;
        }
        

        public override void SetName(string name)
        {
            base.SetName(name);
            //_layerTextureProxy.Name = name;
            //_defaultMaterial.Name = "Layer_" + name;
        }
        //private AssetManager assetMan;
        private static int _width = 1024;//64;
        private static int _height = 1024;//64;

        public int Width
        {
            get { return _width; }
        }

        public int Height
        {
            get { return _height; }
        }

        //public int LayerPosition
        //{
        //    get { return _layerPosition; }
        //    set
        //    {
        //        _layerPosition = value;
        //        FireUpdateChangedEvent(this);
        //    }
        //}
            
        private ICamera _camera;
        public ICamera Camera
        {
            get { return _camera; }

            set 
            {
                //
                // For shared objects, follow this sequence:
                //   1. rewire the notification for when the object
                //        this property points to changes.
                //   2. set the property.
                //   3. perform any internal updates.
                //   4. Implicit in step 3 would be changing this 
                //         object and notifying its subscribers.
                IAssetManaged asset = value as IAssetManaged;
                if (asset != null)
                {
                    IAssetManaged cameraAsset = _camera as IAssetManaged;
                    IAssetManagedHandler changedEvent = new IAssetManagedHandler(camera_ChangedEvent);
                    this.SetPropertyEventHandling(cameraAsset, asset, changedEvent);
                    _camera = value;
                    cameraAsset = _camera as IAssetManaged;
                    cameraAsset.AddChangingEventHandler(changedEvent);
                    camera_ChangedEvent(cameraAsset);
                }
            }
        }

        //private TextureProxy _textureProxy;
        //public TextureProxy TexProxy
        //{
        //    get { return _textureProxy; }
        //}

        private bool _active;
        public bool Active
        {
            get { return _active; }
            set 
            { 
                _active = value;
                FireUpdateChangedEvent(this);
            }
        }

        private List<IDrawable> _drawableProxies;
        public List<IDrawable> Drawables
        {
            get { return _drawableProxies; }
            set 
            { 
                _drawableProxies = value;
                FireUpdateChangedEvent(this);
            }
        }

        public IFrameBufferObject FrameBuffer
        {
            get { return frameBuffer; }
            set
            {
                frameBuffer = value;
                FireUpdateChangedEvent(this);
            }
        }

        public void addDrawableProxy(IDrawable drProxy)
        {
            IAssetManaged asset = drProxy as IAssetManaged;
            _drawableProxies.Add(drProxy);
            if (asset != null)
            {
                IAssetManagedHandler changedEvent = new IAssetManagedHandler(drawable_ChangedEvent);
                asset.AddChangedEventHandler(changedEvent);
                drawable_ChangedEvent(asset);
            }
        }

        public void removeDrawableProxy(IDrawable drProxy)
        {
            if (_drawableProxies.Contains((IDrawable)drProxy))
            {
                _drawableProxies.Remove(drProxy);

                IAssetManaged asset = drProxy as IAssetManaged;
                if (asset != null)
                {
                    IAssetManagedHandler changedEvent = new IAssetManagedHandler(drawable_ChangedEvent);
                    asset.RemoveChangedEventHandler(changedEvent);
                    drawable_ChangedEvent(asset);
                }
            }
        }

        private Bitmap _image;
        
        public Bitmap Image
        {
            get { return _image; }
            set {
                BitmapHandler bmChangedEvent = new BitmapHandler(bitmap_ChangedEvent);
                //this.SetPropertyEventHandling((IAssetManaged)_image, value, changedEvent);
                _image = value;
                //_image.UpdateChangingEvent += new IAssetManagedHandler(bitmap_ChangedEvent);
                bitmap_ChangedEvent(_image);
                }
        }
      

        public Object Clone()
        {
            LayerProxy newLayer = new LayerProxy(this.Name + "copy", this.frameBuffer);
            newLayer.Material = _material;
            newLayer.Camera = this.Camera;
            newLayer.Active = this.Active;
            newLayer.Drawables = new List<IDrawable>(this.Drawables);
            return newLayer;
        }

        #region IDrawable interface
        public IBoundingVolume BoundingVolume
        {
            get { return null; }
        }
        private int _drawablePosition;
        public int DrawablePosition
        {
            get { return _drawablePosition; }
            set
            {
                _drawablePosition = value;
                FireUpdateChangedEvent(this);
            }

        }
        public void Render(IRenderPanel panel)
        {
        }
        #endregion IDrawable interface
        public void Update(IRenderPanel panel)
        {
        }
        private MaterialProxy _material;
        //private IMaterial _material;
        public IMaterial Material
        {
            get { return _material; }
            set
            {
                //
                // For shared objects, follow this sequence:
                //   1. rewire the notification for when the object
                //        this property points to changes.
                //   2. set the property.
                //   3. perform any internal updates.
                //   4. Implicit in step 3 would be changing this 
                //         object and notifying its subscribers.
                IAssetManagedHandler changedEvent = new IAssetManagedHandler(material_ChangedEvent);
                MaterialProxy mat = value as MaterialProxy;
                if (mat != null)
                {
                    this.SetPropertyEventHandling(_material, mat, changedEvent);
                    _material = mat;
                    material_ChangedEvent(mat);
                }
                              
            }
        }

        //public bool ValidMaterial
        //{
        //    get { return _validMaterial; }
        //    set { _validMaterial = value; }
        //}
        
        //private bool _validMaterial = true;

        [field: NonSerializedAttribute()]
        private event IAssetManagedHandler UpdateMaterialChangedEvent;
        [field: NonSerializedAttribute()]
        private event BitmapHandler UpdateBitmapChangedEvent;

        public void AddMaterialChangedEventHandler(IAssetManagedHandler e)
        {
            UpdateMaterialChangedEvent += e;
        }
        public void RemoveMaterialChangedEventHandler(IAssetManagedHandler e)
        {
            UpdateMaterialChangedEvent -= e;
        }
        protected void material_ChangedEvent(IAssetManaged obj)
        {
            FireMaterialUpdateChangedEvent();
        }
        private void FireMaterialUpdateChangedEvent()
        {
            if (eventsHelper == null)
                SetEventsHelper();
            eventsHelper.Fire(UpdateMaterialChangedEvent, this);
        }

        
        protected void camera_ChangedEvent(IAssetManaged obj)
        {
            FireUpdateChangedEvent(this);
        }

        protected void drawable_ChangedEvent(IAssetManaged obj)
        {
            FireUpdateChangedEvent(this);
        }

        public void AddBitmapChangedEventHandler(BitmapHandler e)
        {
            UpdateBitmapChangedEvent += e;
        }
        public void RemoveBitmapChangedEventHandler(BitmapHandler e)
        {
            UpdateBitmapChangedEvent -= e;
        }
        protected void bitmap_ChangedEvent(Bitmap obj)
        {
            FireBitmapUpdateChangedEvent();
        }
        public void FireBitmapUpdateChangedEvent()
        {
            if (eventsHelper == null)
                SetEventsHelper();
            eventsHelper.Fire(UpdateBitmapChangedEvent, this);
        }
        
       


        #region File operations

        //
        // LoadFromFile - static function that loads a transfer function from a file
        //  using binary deserialization.  Returns null if these is a problem.
        //
        public static LayerProxy LoadFromFile(String filename)
        {
            LayerProxy obj;

            Stream stream = File.Open(filename, FileMode.Open);
            BinaryFormatter formatter = new BinaryFormatter();

            try
            {
                obj = (LayerProxy)formatter.Deserialize(stream);
            }
            catch
            {
                obj = null;
            }
            stream.Close();

            return obj;
        }
        //
        // SaveToFile - function that saves a transfer function to a file
        //  using binary serialization.  This is not static and is simply called on the
        //  object that needs to be saved
        //
        public void SaveToFile(String filename)
        {
            //
            // Make a clone to save.  The marshalling done by remoting causes problems with 
            //  serialization.
            //
            LayerProxy obj = (LayerProxy)this.Clone();

            Stream stream = File.Open(filename, FileMode.Create);
            BinaryFormatter formatter = new BinaryFormatter();

            formatter.Serialize(stream, obj);
            stream.Close();
        }
        //
        // We need to preserve the order in the list.
        //
        public override void ReaquireAssets()
        {
            //
            // Use the Properties to ensure the old event handlers are removed and new
            //    ones are added. Particularly if we are setting an item to itself.
            //
            IAssetManaged asset = _camera as IAssetManaged;
            IAssetManaged item;
            if (asset != null)
            {
               item = AssetManager.Instance.Find(typeof(ICamera), asset.Name);
                if (item != asset)
                {
                    this.Camera = item as ICamera;
                }
            }
            item = AssetManager.Instance.Find(_material.GetType(), _material.Name);
            if (item != _material)
            {
                this.Material = item as IMaterial;
            }
            IList<IDrawable> tempList = new List<IDrawable>(_drawableProxies);
            foreach (IDrawable drawable in tempList)
            {
                asset = drawable as IAssetManaged;
                if (asset != null)
                {
                    item = AssetManager.Instance.Find(asset.GetType(), asset.Name);
                    if (item != asset)
                    {
                        this.removeDrawableProxy(drawable);
                        this.addDrawableProxy(item as IDrawable);
                    }
                }
            }
            asset = frameBuffer as IAssetManaged;
            item = AssetManager.Instance.Find(asset.GetType(), asset.Name);
            if (item != asset)
            {
                frameBuffer = item as IFrameBufferObject;
            }
        }
        [OnDeserialized]
        private void OnDeserialized(StreamingContext context)
        {
            IAssetManagedHandler changedEvent = new IAssetManagedHandler(material_ChangedEvent);
            _material.AddChangedEventHandler(changedEvent);
            IAssetManaged asset = _camera as IAssetManaged;
            changedEvent = new IAssetManagedHandler(camera_ChangedEvent);
            asset.AddChangedEventHandler(changedEvent);
            asset.AddChangingEventHandler(changedEvent);
            changedEvent = new IAssetManagedHandler(drawable_ChangedEvent);
            foreach (IDrawable drawable in _drawableProxies)
            {
                asset = drawable as IAssetManaged;
                if (asset != null)
                {
                    asset.AddChangedEventHandler(changedEvent);
                }
            }
            asset = frameBuffer as IAssetManaged;
            asset.AddChangedEventHandler(changedEvent);
        }
        #endregion
    }
}
